package com.example.baseapp.activity

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.baseapp.R
import com.example.baseapp.activity.Fragment.CoroutineFragment
import com.example.baseapp.topmethod.toast
import kotlinx.android.synthetic.main.activity_coroutines.*
import kotlinx.coroutines.*
import kotlin.concurrent.thread

/**
 * @作者：HuaCon
 *
 * @类描述：协程
 *
 */
class CoroutinesActivity : AppCompatActivity() {
    companion object {
        private const val TAG = "CoroutinesActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_coroutines)
        initView()
    }

    private fun initView() {
        bt_createCoroutines.setOnClickListener {
            create10WCoroutines()
        }

        bt_createThread.setOnClickListener {
            create10WThreads()
        }

        bt_crsDomain.setOnClickListener {
            runBlocking {
                launch {
                    Log.d(TAG, "跨域执行")
                    moreLineCode()
                }
            }
        }

        bt_executeQueue.setOnClickListener {
            orderQueue()
        }

        bt_async.setOnClickListener {
            mySum()
        }

        bt_await.setOnClickListener {
            awaitMySum()
        }

        bt_apply.setOnClickListener {
            supportFragmentManager.beginTransaction().apply {
                addToBackStack("coroutinesFragment")
                replace(R.id.root_container, CoroutineFragment())
                commit()
            }
        }
    }

    /**
     * 知识点：CoroutineScope()函数
     * GlobalScope.launch{}每次创建的都是顶层函数，除非你非常明确要创建顶层协程，
     * 否则不太建议使用，主要因为他管理起来成本太高了，如果如下，job调用了cancel()方法就取消协程了
     * 下面这种方式是在实际项目中常用的写法
     */
    private fun mySum() {
        //创建job对象传入CoroutineScope()大写字母的函数中
        val job = Job()

        val coroutineScope = CoroutineScope(job)

        coroutineScope.launch {
            //在调用了async函数后，代码会立刻执行，当调用await()方法时，如果代码块中的代码还没执行完
            //那么await()方法阻塞当前协程，直到可以获得async函数的执行结果
            val start = System.currentTimeMillis()
            val value1 = async {
                delay(1000)
                5 + 5
            }.await()


            val value2 = async {
                delay(1500)
                4 + 6
            }.await()
            val end = System.currentTimeMillis()

            Log.d(TAG, "mySum: ${value1 + value2}")

            // 耗时2.5秒，证明确实是运行完一个协程又运行的另外一个协程
            //但是太耗时了，是否可以 异步计算 ，同步合并呢？
            Log.d(TAG, "spendTime:${end - start}ms")

        }
        //最后可以用这个job对象取消协程作用域中的所有代码
        // job.cancel()
    }

    private fun awaitMySum() {
        //创建job对象传入CoroutineScope()大写字母的函数中
        val job = Job()

        val coroutineScope = CoroutineScope(job)

        coroutineScope.launch {
            //在调用了async函数后，代码会立刻执行，当调用await()方法时，如果代码块中的代码还没执行完
            //那么await()方法阻塞当前协程，直到可以获得async函数的执行结果
            val start = System.currentTimeMillis()
            val value1 = async {
                delay(1000)
                5 + 5
            }

            val value2 = async {
                delay(1500)
                4 + 6
            }
            val end = System.currentTimeMillis()

            Log.d(TAG, "mySum: ${value1.await() + value2.await()}")
            //结果正确，但是耗时少了很多，把上面注释多读几遍
            Log.d(TAG, "spendTime:${end - start}ms")

        }
        //最后可以用这个job对象取消协程作用域中的所有代码
        // job.cancel()
    }

    private fun orderQueue() {
        //runBlocking会阻塞线程，不推荐在实际项目中使用
        runBlocking {
            //coroutineScope只会阻塞当前协程，既不影响其他协程也不影响其他线程
            coroutineScope {
                launch {
                    for (i in 1..10) {
                        Log.d(TAG, "1.for循环:$i ")
                        delay(1000)
                    }
                }
                Log.d(TAG, "4.i am four ")
            }
            Log.d(TAG, "2.coroutineScope run finished (Coroutines) ")
        }
        Log.d(TAG, "3.runBlocking run finished (UI Thread)")

    }

    /**
     * 创建10W个线程
     */
    private fun create10WThreads() {
        val start = System.currentTimeMillis()
        repeat(100000) {
            thread {
                Log.d(TAG, ".")
            }
        }
        val end = System.currentTimeMillis()
        ("十万个线程耗时:${(end - start)}ms").toast(this)
    }

    /**
     * 创建10W个协程
     */
    private fun create10WCoroutines() {
        val start = System.currentTimeMillis()
        //runBlocking 是个挂起函数，因为回阻塞线程，使用时需注意
        //runBlocking会创建一个协程作用域
        runBlocking {
            repeat(100000) {
                launch {
                    Log.d(TAG, "...")
                }
            }
        }
        val end = System.currentTimeMillis()
        ("十万个协程耗时:${(end - start)}ms").toast(this)
    }

    /**
     * 知识点：挂起函数
     * 跨域使用suspend关键字声明为挂起函数
     * 挂起函数见是可以相互调用的
     * 场景：如果有多行代码，需要封装代码，在其他函数中怎么执行呢？
     */
    private suspend fun moreLineCode() {
        Log.d(TAG, "测试")
        //delay是一个非阻塞式的挂起函数，它只会挂起当前协程，并不会影响其他协程
        //delay关键字只能在协程的作用域或者其他挂起函数中调用
        delay(1000)
        //虽然把函数声明成了挂起函数，此时再想开一个launch协程是无法调用的 👉  launch {}
        //因为launch只能在协程作用域使用

        //借助coroutineScope来解决，coroutineScope也是一个挂起函数，因此可以在任何挂起函数中调用
        coroutineScope {
            launch {
                for (i in 0 until 10) {
                    Log.d(TAG, "打印$i")
                    delay(1000)
                }
            }
        }
    }
}

